iT邦幫忙

2025 iThome 鐵人賽

DAY 6
1

前言

昨天我們談到 Cookie 與 Session 的原理:

  • 使用者登入成功後,伺服器會產生一個 Session ID
  • 瀏覽器會把這個 Session ID 存在 Cookie 裡,之後每次請求自動帶上。

今天,我們就用 Spring Boot Security 來實作一次,看看 JSESSIONID 是如何在背後幫我們完成「使用者記住狀態」的。

Step 1. 建立 Spring Boot 專案

Spring boot需要注入依賴,基本上就是匯入別人已經寫好的模組,我們這裡需要匯入的就是Spring boot Security,去使用裡面的功能,詳細可以參考我的這個Github
https://github.com/AnsathSean/spring-security-30days/tree/day06-CookieBased-auth

	<dependency>
   	 	<groupId>org.springframework.boot</groupId>
    	<artifactId>spring-boot-starter-security</artifactId>
	</dependency>
	<dependency>
			<groupId>org.springframework.boot</groupId>
			<artifactId>spring-boot-starter-web</artifactId>
	</dependency>

記得也要匯入最基本的Web模式,大家可以自己去自己的Pom確認有沒有這兩個dependency,

專案的基本設定很簡單,基本上就是這三份程式碼

https://ithelp.ithome.com.tw/upload/images/20250915/20152864RzS2XYK0xh.png

主程式入口(不做任何修改):

SpringSecurity30daysApplication.java

package com.ansathsean.security;

import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;

@SpringBootApplication
public class SpringSecurity30daysApplication {
    public static void main(String[] args) {
        SpringApplication.run(SpringSecurity30daysApplication.class, args);
    }
}

Step 2. 撰寫 Controller

接著,我們要開始建立一個簡單的 API,測試登入前後的差異。

package com.ansathsean.security;

import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RestController;
import jakarta.servlet.http.HttpSession;
import org.springframework.security.core.Authentication;
import org.springframework.security.core.context.SecurityContextHolder;

@RestController
public class Controller {

    @GetMapping("/")
    public String home() {
        return "Welcome to iT鐵人賽系列文!";
    }
    
    @GetMapping("/hello")
    public String hello() {
        return "Hello, you are authenticated with Session!";
    }

    @GetMapping("/public")
    public String publicPage() {
        return "這也是公開 API";
    }

    @GetMapping("/user")
    public String userPage() {
        return "這是 USER 登入後才可以看到的頁面";
    }

    @GetMapping("/admin")
    public String adminPage() {
        return "這是 ADMIN 才能看到的頁面";
    }
    
    @GetMapping("/me")
    public String getSessionInfo(HttpSession session) {
        Authentication auth = SecurityContextHolder.getContext().getAuthentication();
        return "SessionId: " + session.getId() +
               " | User: " + auth.getName() +
               " | Roles: " + auth.getAuthorities();
    }
}

  • 在這裡,我們要測試以下的API
  • / → 公開 API(任何人都能存取)
  • /public → 公開 API(任何人都能存取)受保護 AI(需要登入 Session 才能存取)
  • /user →受保護 AI(需要user登入 Session 才能存取)
  • /admin →受保護 AI(需要admin登入 Session 才能存取)

Step 3. 設定 SecurityConfig

Spring boot Security本身就是讓大家快速建立授權跟認證而使用的,如果要使用Spring boot Security的套件,我們就要先建立SecurityConfig,然後在裡面設定我們要保護的API,跟我們要使用的加密方法,以及我們的帳號密碼。在這個階段為了展示,我們簡化了帳號密碼設定,將密碼直些寫到設定檔中,如果要進階使用的話,就要拆開來,把帳密存到DB裡面取用。

package com.ansathsean.security;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.security.config.Customizer;
import org.springframework.security.config.annotation.web.configuration.EnableWebSecurity;
import org.springframework.security.config.annotation.web.builders.HttpSecurity;
import org.springframework.security.core.userdetails.User;
import org.springframework.security.core.userdetails.UserDetailsService;
import org.springframework.security.provisioning.InMemoryUserDetailsManager;
import org.springframework.security.crypto.bcrypt.BCryptPasswordEncoder;
import org.springframework.security.crypto.password.PasswordEncoder;
import org.springframework.security.web.SecurityFilterChain;

@Configuration
@EnableWebSecurity
public class SecurityConfig {

    @Bean
    SecurityFilterChain securityFilterChain(HttpSecurity http) throws Exception {
        http
                .authorizeHttpRequests(auth -> auth
                        .requestMatchers("/", "/public").permitAll()
                        .requestMatchers("/user").hasRole("USER")
                        .requestMatchers("/admin").hasRole("ADMIN")
                        .anyRequest().authenticated()
                )
                .formLogin(Customizer.withDefaults()) // 啟用表單登入
                .logout(Customizer.withDefaults())    // 登出
                .csrf(csrf -> csrf.disable());
               // .csrf(Customizer.withDefaults());     // 啟用 CSRF 防護

        return http.build();
    }

    @Bean
    UserDetailsService userDetailsService(PasswordEncoder passwordEncoder) {
        return new InMemoryUserDetailsManager(
                User.withUsername("user")
                        .password(passwordEncoder.encode("password"))
                        .roles("USER")
                        .build(),
                User.withUsername("admin")
                        .password(passwordEncoder.encode("admin123"))
                        .roles("ADMIN")
                        .build()
        );
    }

    @Bean
    PasswordEncoder passwordEncoder() {
        return new BCryptPasswordEncoder();
    }
}

這裡我們做了幾件事:

  1. 建立記憶體使用者:我們建立了user、admin的帳號
  2. / API 可以匿名訪問,/user/adminAPI 需要登入。
  3. 啟用 Spring Security 預設的登入表單(自動產生 /login 頁面)。

Step 4. 測試流程

接著讓我們啟動專案。

啟動專案後,打開 Postman 測試:

  1. 訪問 http://localhost:8080/
    • 直接回應:

      Welcome to iT鐵人賽系列文!
      

https://ithelp.ithome.com.tw/upload/images/20250915/20152864H0KqI8C8ad.png

  1. 訪問 /user(未登入)
    • 會被 Spring Security 攔截 → 自動導向 /login
    • Postman 會顯示 302 Redirect 或回傳登入表單的 HTML。

https://ithelp.ithome.com.tw/upload/images/20250915/20152864xnufohoFq5.png

從Postman裡面會看到這些內容,如果從網頁瀏覽器的話就會看到這個登入畫面,代表我們需要登入才有權限看到後面的東西

https://ithelp.ithome.com.tw/upload/images/20250915/20152864G0NUSZprGU.png

  1. 登入 /login
    • 我們在 Body 帶入帳密,在PostMan中記得把方法改成Post:

      username=user
      password=password
      
      

https://ithelp.ithome.com.tw/upload/images/20250915/20152864aF7SZWG9WE.png

- 登入成功後,伺服器回傳 Header,而這個就是我們的Session cookie:
    
    ```
    Set-Cookie: JSESSIONID=xxxxxx
    ```
    
  1. 再次訪問 /user(已登入)
    • 瀏覽器 / Postman 會自動帶上 JSESSIONID ,我們不需要再使用body的帳號密碼,記得要改回Get
    • 成功回應:

https://ithelp.ithome.com.tw/upload/images/20250915/201528646bMhozwEmc.png

觀察 JSESSIONID

我們在controller中開發了一個/me的功能,讓我們可以從這個API中取得我們的Session資訊,我們接著輸入這個網址,就能夠拿到以下內容,他會呈現我們的SessionID,以及我們的User名稱、User的腳色資訊。

https://ithelp.ithome.com.tw/upload/images/20250915/20152864CSAvRuLIzo.png

這就是 Session ID,它讓伺服器能辨識「這個人已經登入過了」。

結果與限制

優點:

  • 使用者體驗好 → 登入一次即可,之後瀏覽器自動帶 Cookie。
  • 安全性比 Basic Auth 高(不用每次都傳帳號密碼)。

限制:

  • Session 儲存在伺服器端 → 使用者多時,伺服器壓力大。
  • 分散式架構下(多台伺服器)需要 Session 同步 或 Redis 儲存。
  • 有 CSRF / Session 劫持風險,需要額外防護。

這也是為什麼後來會發展出 Token / JWT,把狀態轉移到客戶端,減輕伺服器負擔。

總結

今天我們透過 Spring Boot Security 成功體驗了 Cookie + Session 機制

  • 登入後伺服器建立 Session → 產生 JSESSIONID
  • 瀏覽器自動帶 Cookie → API 請求能辨識使用者
  • 這就是大部分傳統網站(例如 PHP、Java Web App)的登入機制

明日預告

明天,我們要進入 Token Authentication(概念篇),來看看為什麼在分散式架構與 API 服務中,Token 會比 Session 更方便、更彈性。

大家可以慢慢吸收,慢慢了解,我們就是慢慢等,慢慢等,等紅燈,變綠燈~

我們明天見!


上一篇
Day 5 Cookie Based Authentication (Session 機制)
下一篇
Day 7 Token Authentication (概念篇)
系列文
「站住 口令 誰」關於資安權限與授權的觀念教學,以Spring boot Security框架實作11
圖片
  熱門推薦
圖片
{{ item.channelVendor }} | {{ item.webinarstarted }} |
{{ formatDate(item.duration) }}
直播中

尚未有邦友留言

立即登入留言